// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "synch.h"
#include "syscall.h"
#include <stdio.h>
#include <iostream>
#include "machine.h"
#include "process.h"
#include "processTable.h"
#include "../vm/tableinfo.h"
#include <sstream>

#ifdef NETWORK
#include "network.h"
#include "post.h"
#endif

using namespace std;

int myexp ( int count ) {
  int i, val=1;
  for (i=0; i<count; i++ ) {
    val = val * 10;
  }
  return val;
}

void itoa( char arr[], int size, int val ) {
  int i, max, dig, subval, loc;
  for (i=0; i<size; i++ ) {
    arr[i] = '\0';
  }

  for ( i=1; i<=2; i++ ) {
    if (( val / myexp(i) ) == 0 ) {
      max = i-1;
      break;
    }
  }

  subval = 0;
  loc = 0;
  for ( i=max; i>=0; i-- ) {
    dig = 48 + ((val-subval) / myexp(i));
    subval = (dig-48) * myexp(max);
    arr[loc] = dig;
    loc++;
  }

  return;
}



int copyin(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes from the current thread's virtual address vaddr.
    // Return the number of bytes so read, or -1 if an error occors.
    // Errors can generally mean a bad virtual address was passed in.
    bool result;
    int n=0;			// The number of bytes copied in
    int *paddr = new int;

    while ( n >= 0 && n < len) {

      result = machine->ReadMem( vaddr, 1, paddr );

      buf[n++] = *paddr;

      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    delete paddr;
    return len;
}

int copyout(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes to the current thread's virtual address vaddr.
    // Return the number of bytes so written, or -1 if an error
    // occors.  Errors can generally mean a bad virtual address was
    // passed in.
    bool result;
    int n=0;			// The number of bytes copied in

    while ( n >= 0 && n < len) {
      // Note that we check every byte's address
      result = machine->WriteMem( vaddr, 1, (int)(buf[n++]) );

      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    return n;
}

void Create_Syscall(unsigned int vaddr, int len) {
    // Create the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  No
    // way to return errors, though...
    char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Create\n");
	delete buf;
	return;
    }

    buf[len]='\0';

    fileSystem->Create(buf,0);
    delete[] buf;
    return;
}

int Open_Syscall(unsigned int vaddr, int len) {
    // Open the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  If
    // the file is opened successfully, it is put in the address
    // space's file table and an id returned that can find the file
    // later.  If there are any errors, -1 is returned.
    char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *f;			// The new open file
    int id;				// The openfile id

    if (!buf) {
	printf("%s","Can't allocate kernel buffer in Open\n");
	return -1;
    }

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Open\n");
	delete[] buf;
	return -1;
    }

    buf[len]='\0';

    f = fileSystem->Open(buf);
    delete[] buf;

    if ( f ) {
	if ((id = currentThread->space->fileTable.Put(f)) == -1 )
	    delete f;
	return id;
    }
    else
	return -1;
}

void Write_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one. For disk files, the file is looked
    // up in the current address space's open file table and used as
    // the target of the write.
    
    char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output

    /*IntStatus old = interrupt->SetLevel(IntOff);*/

    if ( id == ConsoleInput) return;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer for write!\n");
	return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
	    printf("%s","Bad pointer passed to to write: data not written\n");
	    delete[] buf;
	    return;
	}
    }

    if ( id == ConsoleOutput) {
      for (int ii=0; ii<len; ii++) {
	printf("%c",buf[ii]);
      }

    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    f->Write(buf, len);
	} else {
	    printf("%s","Bad OpenFileId passed to Write\n");
	    len = -1;
	}
    }

    delete[] buf;
    /* (void)interrupt->SetLevel(old);*/

}

void PrintMsg_Syscall(unsigned int vaddr, int len) {
  char* msg = new char[len];
  int check = -1;

  while (check == -1){
    check = copyin(vaddr, len, msg);
  }
  
  cout<<msg<<endl;
}

int Read_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one.    We reuse len as the number of bytes
    // read, which is an unnessecary savings of space.
    char *buf;		// Kernel buffer for input
    OpenFile *f;	// Open file for output

    if ( id == ConsoleOutput) return -1;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer in Read\n");
	return -1;
    }

    if ( id == ConsoleInput) {
      //Reading from the keyboard
      scanf("%s", buf);

      if ( copyout(vaddr, len, buf) == -1 ) {
	printf("%s","Bad pointer passed to Read: data not copied\n");
      }
    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    len = f->Read(buf, len);
	    if ( len > 0 ) {
	        //Read something from the file. Put into user's address space
  	        if ( copyout(vaddr, len, buf) == -1 ) {
		    printf("%s","Bad pointer passed to Read: data not copied\n");
		}
	    }
	} else {
	    printf("%s","Bad OpenFileId passed to Read\n");
	    len = -1;
	}
    }

    delete[] buf;
    return len;
}

void Close_Syscall(int fd) {
    // Close the file associated with id fd.  No error reporting.
    OpenFile *f = (OpenFile *) currentThread->space->fileTable.Remove(fd);

    if ( f ) {
      delete f;
    } else {
      printf("%s","Tried to close an unopen file\n");
    }
}

#ifdef NETWORK

#endif

//sendMessage
#ifdef NETWORK
bool sendMessage(char* data){
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
 
  outPktHdr.to = 0;		
  outMailHdr.to = 0;
  outMailHdr.from = 1;
  outMailHdr.length = strlen(data) + 1;
  
  //send message
  bool success = postOffice->Send(outPktHdr, outMailHdr, data); 
  return success;
}
#endif

#ifdef NETWORK
int msgParser(char msg[]){
  char* msgCopy = new char(sizeof(msg));
  char* delimiter = new char(' ');
  msgCopy = strtok(msg, delimiter); // get the first token, an integer represented by ascii characters
  fflush(stdout);
  int x = atoi(msgCopy);
  cout << endl << endl << "Here is value returned to client " 
       << x << endl;
  return x; // convert that first token to an int value and return it
}

#endif

void mergeMsgInt (unsigned int virtadd, int len, int arg)
{
  int check = -1; //set to -1 so we can run our loop for copyin
  int check2 = -1; //set to -1 so we can run our loop for copyin
  char* msg = new char[len];
  char* numChar = new char[2];
  char* newMsg = new char[len + 2];
  char* p = "^";
  stringstream ss;

  //read in the char array from the user program
  while (check == -1){
    check = copyin(virtadd, len, msg);
  }

  //convert the integer to a character array
  itoa (numChar, 3, arg);

  //concatenate the msg into one string
  ss<<msg;
  ss<<numChar;
  ss<<p;
  //put that new string into a new char array
  ss>>newMsg;

  cout<<"here is the new msg: "<<newMsg<<endl;
  //write message back to the user program's address space

  //return newMsg;
  check = check + 3;
  while (check2 == -1) {
    check2 = copyout(virtadd, check, newMsg);
  }
}

void Send_Syscall (PacketHeader inPktHdr, MailHeader inMailHdr, unsigned int vaddr, int len) {

  int check = -1;
  char* msg = new char[MaxMailSize];

  PacketHeader outPktHdr;
  MailHeader outMailHdr;

  //read in the char array from the user program
  while (check == -1){
    check = copyin(vaddr, len, msg);
  }

  outPktHdr.to = inPktHdr.to;
  outMailHdr.to = inMailHdr.to;
  outMailHdr.from = inMailHdr.from;
  outMailHdr.length = strlen(msg) + 1;

  bool success = postOffice->Send(outPktHdr, outMailHdr, msg);

  if (!success) {
    cout<<"Message not sent"<<endl;
    interrupt->Halt();
  } else {
    cout<<"Message Sent"<<endl;
  }
}


int receiveMsg(char *m){
  char* msgCopy = new char(*m);
  char* delimiter = new char(' ');
  char* token = strtok(msgCopy,delimiter);
  int r;
  while(token != NULL){
    r = atoi(token);
    token = (NULL," ");
  }
  return r;
} 

//create Lock syscall
int CreateLock_Syscall(int name)  {
  //Code for network
#ifdef NETWORK
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  int response = -1;
  sprintf(data,"0 Lock %d",name);

  char buffer[MaxMailSize];  

  //send message
  bool success = sendMessage(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }
  else{
    cout<<"Sent msg to create Lock\n";
  }

  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  response = msgParser(buffer);
  fflush(stdout);
  cout<<"Create Lock: "<<response<<endl;
  return response;
  
#endif

#ifndef NETWORK
  Lock* test = new Lock("Creating a Lock"); //instantiate the lock

  int index = -1; //set to negative so that it cannot overwrite another

  for (int i = 0; i<100; i++){
    if (lockArray[i] == NULL){
      lockArray[i] = test; //put the new lock into the array
      index = i; //set the index value
      //printf("lock created at index: %d\n", index);
      break;//get out of the loop
    }
  }

  return index; //send the index back to the user prog
#endif
}//end Create Lock




int CreateList_Syscall()  {

  List* test = new List(); //instantiate the lock

  int index = -1; //set to negative so that it cannot overwrite another

  for (int i = 0; i<10; i++){
    if (listArray[i] == NULL){
      listArray[i] = test; //put the new lock into the array
      index = i; //set the index value
      //printf("list created at index: %d\n", index);
      break;//get out of the loop
    }
  }

  return index; //send the index back to the user prog
}

/***Create Condition ***/
int CreateCondition_Syscall(int name)  {
#ifdef NETWORK
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  int response = -1;
 
  sprintf(data,"4 Cond %d",name);
  char buffer[MaxMailSize];  
  //send message
  bool success = sendMessage(data); 
  if ( !success ) {
    printf("Create condition: The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }
  else{
    cout<<"Sent message to create condition\n";
  }
  //receiving the ACK message back
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);

  response = msgParser(buffer);
  fflush(stdout);
  cout<<"Create Cond: "<<response<<endl;
  return response;


#endif
#ifndef NETWORK
  Condition* test = new Condition("Creating a Condidtion"); //instantiate the lock

  int index = -1; //set to negative so that it cannot overwrite another

  for (int i = 0; i<100; i++){
    if (cvArray[i] == NULL){
      cvArray[i] = test; //put the new lock into the array
      index = i; //set the index value
      //printf("CV created at index: %d\n", index);
      break;//get out of the loop
    }
  }
  return index; //send the index back to the user prog

#endif
}

/*****************************************************/
//This method will just print out a passed integer
void PrintInt_Syscall(int num){
  if(num >= 0 && num < 1000){
    cout<<num;
  }
  else{
    cout <<num<<endl;
    cout<<"PrintInt: something is wrong"<<endl;}
}

/****************************************************************/
//this method will add a value num to the list array with size len
void AddtoList_Syscall(int num, int len) {

  if (len >= 10 || len < 0) {
    printf ("Bad index from user Program\n");
  } else {
    List* temp = listArray[len];
    temp->Append((void*)num);//append the value
  }
}

//this method will remove a value num to the list array with size len
int RemovefromList_Syscall(int len) {

  if (len >= 10 || len < 0) {
    printf ("Bad index from user Program\n");
  } else {
    List* temp = listArray[len];
    return (int)temp->Remove();//remove the first value
  }
  return -1; //if there was a problem
}


//system call to get a lock
void Acquire_Syscall(int len) {
#ifdef NETWORK
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  int response = -1;
  sprintf(data,"1 Lock %d",len);
  char buffer[MaxMailSize];  

  //send message
  bool success = sendMessage(data);
  if ( !success ) {
    printf("Acquire: The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }
  else{
    cout<<"Sent message to acquire lock\n";
  }

  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  fflush(stdout);

#endif

#ifndef NETWORK 
  if (len >= 100 || len < 0) {
    printf ("Bad index from user Program\n");//if bad input was passed
  } else {
    Lock * temp = lockArray[len];//get a pointer to the requested lock
    if (temp != NULL){
      temp->Acquire();//acquire the lock
    } else {
      printf("There is no lock with that index\n");//print message if lock
                                                   //doesn't exist
    }
  }


#endif 

}

//system call to release the lock
void Release_Syscall(int len) {
#ifdef NETWORK

  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  int response = -1;
  sprintf(data,"2 Lock %d",len);
 
  char buffer[MaxMailSize];  

  //send message
  bool success = sendMessage(data);
  if ( !success ) {
    printf("Release: The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }
  else{
    cout<<"Sent message to release lock\n";
  }

  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  
  fflush(stdout);
#endif
#ifndef NETWORK 


  if (len >= 100 || len < 0) {
    printf ("Bad index from user Program\n");//if bad input was passed
  } else {
    Lock * temp = lockArray[len];//get a pointer to the requested lock
    if (temp != NULL){
      temp->Release();//release the lock
    } else {
      printf("There is no lock with that index\n");
    }
  }

#endif
}

//deallocate the lock
void DestroyLock_Syscall(int len) {

  if (len >= 100 || len < 0) {
    printf ("Bad index from user Program\n");//if bad input was passed
  } else {
    Lock * temp = lockArray[len];//get a pointer to the requested lock
    if (temp != NULL){
      delete temp;
      lockArray[len] = NULL;//set lock to null
      printf("Lock destroyed Successfully\n");//print message
    } else {
      printf("There is no lock with that index\n");
    }
  }
}

//deallocate the cv
void DestroyCondition_Syscall(int len) {

  if (len >= 100 || len < 0) {
    printf ("Bad index from user Program\n");//if bad input was passed
  } else {
    Condition * temp = cvArray[len];//get a pointer to the requested cv
    if (temp != NULL){
      delete temp;
      cvArray[len] = NULL;//set lock to null
      printf("Condition destroyed Successfully\n");//print message
    } else {
      printf("There is no condition with that index\n");
    }
  }
}

void Wait_Syscall(int Lnum, int Cnum) {
#ifdef NETWORK
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  int response = -1;

  sprintf(data,"5 Lock %d Cond %d",Lnum,Cnum);
  char buffer[MaxMailSize];  

  //send message
  bool success = sendMessage(data);
  if ( !success ) {
    printf("Wait: The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }
  else{
    cout<<"Client: Sent message to wait\n";
  }

  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
 
  fflush(stdout);

#endif
#ifndef NETWORK
  if (Cnum < 0 || Cnum >= 100) {
    cout<<"Bad input from user prog"<<endl;//test input from user program
  } else if (Lnum < 0 || Lnum >= 100) {
    cout<<"Bad input from user prog"<<endl;
  }
  else {
    Lock *locktemp = lockArray[Lnum];//get requested lock
    Condition *cvtemp = cvArray[Cnum];//get requested cv

    if(cvtemp == NULL)
      cout<<"CV does not exist "<<endl;//check to make sure both exist
    else if(locktemp == NULL)
      cout<<"Lock does not exist "<<endl;
    else
      {
	cvtemp->Wait(locktemp);//wait
      }
  }
#endif
}

void Signal_Syscall(int Lnum, int Cnum) {
#ifdef NETWORK
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  int response = -1;

  sprintf(data,"6 Lock %d Cond %d",Lnum,Cnum);
  char buffer[MaxMailSize];  

  //send message
  bool success = sendMessage(data);
  if ( !success ) {
    printf("Signal: The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }
  else{
    cout<<"Client: Sent message to signal\n";
  }

  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
 
  fflush(stdout);
#endif
#ifndef NETWORK
  if (Cnum < 0 || Cnum >= 100) {
    cout<<"Bad input from user prog"<<endl;//test input from user program
  } else if (Lnum < 0 || Lnum >= 100) {
    cout<<"Bad input from user prog"<<endl;
  }
  else {
    Lock *locktemp = lockArray[Lnum];//get requested lock
    Condition *cvtemp = cvArray[Cnum];//get requested cv

    if(cvtemp == NULL)
      cout<<"CV does not exist "<<endl;//check to make sure both exist
    else if(locktemp == NULL)
      cout<<"Lock does not exist "<<endl;
    else
      cvtemp->Signal(locktemp);//signal
  }

#endif
}

void Broadcast_Syscall(int Lnum, int Cnum) {
#ifdef NETWORK
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  int response = -1;

  sprintf(data,"7 Lock %d Broadcast %d",Lnum,Cnum);
  // data = "5 Lock 1 Cond 0";
  char buffer[MaxMailSize];  

  //send message
  bool success = sendMessage(data);
  if ( !success ) {
    printf("Wait: The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }
  else{
    cout<<"Client: Sent message to broadcast\n";
  }

  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
 
  fflush(stdout);

#endif
#ifndef NETWORK
  if (Cnum < 0 || Cnum >= 100) {
    cout<<"Bad input from user prog"<<endl;//test input from user program
  } else if (Lnum < 0 || Lnum >= 100) {
    cout<<"Bad input from user prog"<<endl;
  }
  else {
    Lock *locktemp = lockArray[Lnum];//get requested lock
    Condition *cvtemp = cvArray[Cnum];//get requested cv

    if(cvtemp == NULL)
      cout<<"CV does not exist "<<endl;//check to make sure both exist
    else if(locktemp == NULL)
      cout<<"Lock does not exist "<<endl;
    else
      cvtemp->Broadcast(locktemp);//broadcast
  }
#endif
}

void Yield_Syscall () {
  currentThread->Yield();
}

//function to start a new kernel thread
void kernel_Thread(int num) {
  currentThread->space->InitRegisters();//initialize registers to 0

  machine->WriteRegister (PCReg, num);//set pcreg with vaddress

  machine->WriteRegister(NextPCReg, num + 4);//set next pcreg
  
  currentThread->space->RestoreState();//restore state
  
  //int stackPos = currentThread->space->allocateStack();//get some stack space

  //  currentThread->setStackPTR(stackPos);//set currentThread stack first page

  //machine->WriteRegister(StackReg, stackPos);//set the stack reg

  machine->Run();//start the thread
}

void Fork_Syscall(int num) {
  if (numberThreads <= 49) {
    numberThreads ++;//increment the number of threads
    
    Thread* newThread = new Thread ("Thread");//instantiate a new thread
    
    newThread->setPID(currentThread->getPID());//set that threads process id
    
    Process* currentProcess = ptable->getProcess(currentThread->getPID());//pointer to current process
    
    int id = currentProcess->addThread(newThread);//add the new thread to the process
    
    newThread->setID (id);//set the new threads id
    
    newThread->space = currentThread->space;//set the new threads addrspace
    
    newThread->Fork((VoidFunctionPtr)kernel_Thread, num);//fork the new thread
  }
}


void exec_thread(){
  currentThread->space->InitRegisters();
  currentThread->space->RestoreState();
  machine->Run();
}

//length is the length of the argument passed from the exec call
int Exec_Syscall(int vaddress, int length) {

  int pID = -1;
  char * filename = new char[length];//buffer to hold the char array
  int num = -1;

  //this loop will convert the vaddress passed into the char array to open
  while (num == -1) {
    num = copyin((unsigned)vaddress, length, filename);//convert to char array
  }
  
  OpenFile *executable = fileSystem->Open(filename);//open the given file
  
  if (executable == NULL) {//if the file does not open successfully
    printf("Unable to open file %s\n", filename);
  }
  
  AddrSpace *newSpace = new AddrSpace(executable);//allocate a new addrspace
  
  Thread* newThread = new Thread ("process thread");//create a process thread
  Process *newProcess = new Process (newSpace);//create a new process

  numberProcess++;//increment the number of processes

  newThread->space = newSpace;//set process thread's addrspace

  newThread->setID(newProcess->addThread(newThread));//set the process thread id
  pID = ptable->addProcess (newProcess);//add the new process to the process table

  newThread->setPID (pID);

  //  newThread->setStackPTR(newThread->space->allocateStack());

  newThread->Fork((VoidFunctionPtr)exec_thread, 0);//start the new process

  return pID;//return the space id
}

void Exit_Syscall(int status) {
  
  if (status != 0)
    cout<<"Value: "<<status<<endl;

  exitLock->Acquire();//get the lock

  //nachos thread id is set to -5 in progtest.cc
  //only that thread can have an id of -5 so only
  //the nachos thread can get into this if to halt
  if (currentThread->getID() == -5 && numberProcess != 0) {
    cout<<"waiting"<<endl;
    exitCV->Wait (exitLock);
    cout<<"halt signal received"<<endl;

    interrupt->Halt();
  }else if (currentThread->getID() == -5 && numberProcess == 0) {
    cout<<"no process"<<endl;
    interrupt->Halt();
  }
  exitLock->Release();


  //get a pointer to the current process
  Process* currentProcess = ptable->getProcess(currentThread->getPID());
  
  //check if there is only one thread in the process
  //if there is only one that is the process thread

  exitLock->Acquire();//get the lock

  if (currentProcess->getNumThreads() == 1){  

    numberProcess --;//decrement number of processes

    //check if this is the last process if it is 
    //signal the nachos thread to halt
    if (numberProcess == 0) {
      cout<<"signal"<<endl;
      exitCV->Signal(exitLock);
     }

    //    currentThread->space->deallocateStack(currentThread->getStackPTR()/PageSize);//clear the stack for the thread

    //currentProcess->deallocateSpace(currentProcess->getSpace());//clear the page table

    currentProcess->removeThread(currentThread);//remove thread from the process

    exitLock->Release();//release the lock

    currentThread->Finish();//finish current thread

  } else {

    //currentThread->space->deallocateStack(currentThread->getStackPTR()/PageSize);//clear the stack 
    currentProcess->removeThread(currentThread);//remove thread from the process
    exitLock->Release();//release the lock
    currentThread->Finish();//finish current thread
  }
}
  
void ExceptionHandler(ExceptionType which) {
    int type = machine->ReadRegister(2); // Which syscall?
    int rv=0; 	// the return value from a syscall
    if ( which == SyscallException ) {
      switch (type) {
      default:
	DEBUG('a', "Unknown syscall - shutting down.\n");
      case SC_Halt:
	DEBUG('a', "Shutdown, initiated by user program.\n");
	interrupt->Halt();
	break;
      case SC_Create:
	DEBUG('a', "Create syscall.\n");
	Create_Syscall(machine->ReadRegister(4), 
		       machine->ReadRegister(5));
	break;
      case SC_Open:
	DEBUG('a', "Open syscall.\n");
	rv = Open_Syscall(machine->ReadRegister(4), 
			  machine->ReadRegister(5));
	break;
      case SC_Write:
	DEBUG('a', "Write syscall.\n");
	Write_Syscall(machine->ReadRegister(4),
		      machine->ReadRegister(5),
		      machine->ReadRegister(6));
	break;
      case SC_Read:
	DEBUG('a', "Read syscall.\n");
	rv = Read_Syscall(machine->ReadRegister(4),
			  machine->ReadRegister(5),
			  machine->ReadRegister(6));
	break;
      case SC_Close:
	DEBUG('a', "Close syscall.\n");
	Close_Syscall(machine->ReadRegister(4));
	break;
      case SC_CreateLock:
	DEBUG('a', "CreateLock syscall.\n");
	rv = CreateLock_Syscall(machine->ReadRegister(4));
	break;
      case SC_Acquire:
	DEBUG('a', "Acquire syscall.\n");
	Acquire_Syscall(machine->ReadRegister(4));
	break;
      case SC_PrintInt:
	DEBUG('a', "Print an Integer.\n");
	PrintInt_Syscall(machine->ReadRegister(4));
	break;
      case SC_Release:
	DEBUG('a', "Release syscall.\n");
	Release_Syscall(machine->ReadRegister(4));
	break;
      case SC_DestroyLock:
	DEBUG('a', "Destroy Lock syscall.\n");
	DestroyLock_Syscall(machine->ReadRegister(4));
	break;
      case SC_CreateCondition:
	DEBUG('a', "CreateCondition syscall.\n");
	rv = CreateCondition_Syscall(machine->ReadRegister(4));
	break;
      case SC_DestroyCondition:
	DEBUG('a', "DestroyCondition syscall.\n");
	DestroyCondition_Syscall(machine->ReadRegister(4));
	break;
      case SC_Wait:
	DEBUG('a', "Wait syscall.\n");
	Wait_Syscall(machine->ReadRegister(4),
		     machine->ReadRegister(5));
	break;
      case SC_Signal:
	DEBUG('a', "Signal syscall.\n");
	Signal_Syscall(machine->ReadRegister(4),
		       machine->ReadRegister(5));
	break;
      case SC_Broadcast:
	DEBUG('a', "Broadcast syscall.\n");
	Broadcast_Syscall(machine->ReadRegister(4),
			  machine->ReadRegister(5));
	break;
      case SC_Yield:
	DEBUG('a', "Yield syscall.\n");
	Yield_Syscall();
	break;
      case SC_CreateList:
	DEBUG('a', "CreateList syscall.\n");
	rv = CreateList_Syscall();
	break;
      case SC_AddtoList:
	DEBUG('a', "AddtoList syscall.\n");
	AddtoList_Syscall(machine->ReadRegister(4),
			  machine->ReadRegister(5));
	break;
      case SC_RemovefromList:
	DEBUG('a', "RemovefromList syscall.\n");
	rv = RemovefromList_Syscall(machine->ReadRegister(4));
	break;
      case SC_Fork:
	DEBUG('a', "Fork syscall.\n");
	Fork_Syscall(machine->ReadRegister(4));
	break;
      case SC_Exit:
	DEBUG('a', "Exit syscall.\n");
	Exit_Syscall(machine->ReadRegister(4));
	break;
      case SC_Exec:
	DEBUG('a', "Exec syscall.\n");
	Exec_Syscall(machine->ReadRegister(4),
		     machine->ReadRegister(5));
	break;
      case SC_mergeMsgInt:
	//DEBUG("a", "mergeMsgInt syscall.\n");
	mergeMsgInt(machine->ReadRegister(4),
		    machine->ReadRegister(5),
		    machine->ReadRegister(6));
	break;
      case SC_PrintMsg:
	DEBUG('a', "Print syscall.\n");
	PrintMsg_Syscall(machine->ReadRegister(4),
		     machine->ReadRegister(5));
	break;
      }

      
      
      // Put in the return value and increment the PC
      machine->WriteRegister(2,rv);
      machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));
      machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));
      machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);
      return;
    } 
    //for page fault exceptions
    else if (which == PageFaultException) {
      IntStatus oldLevel = interrupt->SetLevel(IntOff);		//disable the interrupts

      
      for(int i = 0; i < 4; i++){
	if(machine->tlb[i].use == true){
	  int page = machine->tlb[i].physicalPage;
	  IPT[page].use = true;
	}
	if(machine->tlb[i].dirty == true){
	  int page = machine->tlb[i].physicalPage;
	  IPT[page].dirty = true;
	}
      }
      

      unsigned int virtpn = machine->ReadRegister(39)/PageSize;//get the virt page
      unsigned int physpn = 0;
      int found = 0;
      TableInfo* pageTable = currentThread->space->getPageTable();
      int mySum = 0;
      
      
      // If it is not in the TLB, check the IPT
      //cout<<"Found: "<<found<<endl;
      
      if(found == 0){
	for(int i = 0; i < NumPhysPages; i++){
	  
	  if(IPT[i].virtualPage == virtpn && IPT[i].ProcessID == currentThread->space && IPT[i].valid == true){
	    found = 1;
	    physpn = i;
	    break;
	  }
	}
      
	// If found in IPT, update the TLB
	if(found == 1){
	  machine->tlb[currentTLBSpot].virtualPage = virtpn;
	  machine->tlb[currentTLBSpot].physicalPage = physpn;
	  machine->tlb[currentTLBSpot].valid = TRUE;
	  machine->tlb[currentTLBSpot].use = IPT[physpn].use;
	  machine->tlb[currentTLBSpot].dirty = IPT[physpn].dirty;
	  machine->tlb[currentTLBSpot].readOnly = IPT[physpn].readOnly;
	  
	  currentTLBSpot = (currentTLBSpot + 1) % 4;
	}
      }      

      //if its not in the IPT find a page to load it into
      if (found != 1){

	physMapLock->Acquire();
	int openPage = physMap->Find();
	physMapLock->Release();
	
	//	cout<<"openPage: "<<openPage<<endl;
	//if physical memory is full open page will be -1
	if (openPage == -1) {
	  
	  //for fifo
	  if (fifo) {
	    openPage = fifospot;
	    fifospot = (fifospot + 1) % 32;
	  }

	  //for rand
	  if (!fifo) {
	    openPage = rand() % 32;
	  }

	  if (true) {

	    swapLock->Acquire();
	    //find an open page within the swap file
	    int freeSwapPage = swapFileMap->Find();
	    if(freeSwapPage == -1) {
	      cout<<"asdf"<<endl;
	      interrupt->Halt();
	    }
	    //write that page to the swap file
	    swapFile->WriteAt(&(machine->mainMemory[openPage * PageSize]),
			      PageSize, freeSwapPage * PageSize);

	    pageLock->Acquire();
	    int vpn = IPT[openPage].virtualPage;
	    
	    //do the following steps to make sure you get the correct page table
	    AddrSpace* tempSpace = (AddrSpace*)IPT[openPage].ProcessID;
	    TableInfo * tempTable = tempSpace->getPageTable();

	    //cout<<"Write: "<<IPT[openPage].ProcessID<<" "<<vpn<<" "<<freeSwapPage<<endl;
	    mySum=0;
	    for (int ijk=0;ijk<PageSize;ijk++) {
	      mySum+=machine->mainMemory[openPage*PageSize+ijk];
	    }
	    //	    cout<<"CRC write: "<<mySum<<endl;
	    //this is the correct page table
	    tempTable[vpn].location = 1;//1 means its in the swap file
	    
	    //store the location in the swap file of that page
	    tempTable[vpn].swapLoc = freeSwapPage;

	    //	    cout<<"Page: "<<tempTable[vpn].physicalPage<<" "<<openPage<<endl;
	    pageLock->Release();

	    swapLock->Release();
	  }else {
	    //if the page was not dirty just update the page table for the swapped page

	    pageLock->Acquire();
	    int vpn = IPT[openPage].virtualPage;
	    
	    //do the following steps to make sure you get the correct page table
	    AddrSpace* tempSpace = (AddrSpace*)IPT[openPage].ProcessID;
	    TableInfo * tempTable = tempSpace->getPageTable();

	    //this is the correct page table
	    tempTable[vpn].location = 0;//0  means its in the executable
	    
	    //store the location in the swap file of that page
	    pageLock->Release();
	  }

	  //check if the evicted page is in the TLB
	  for (int i = 0; i < 4; i++){
	    if (machine->tlb[i].physicalPage == openPage)
	      machine->tlb[i].valid = false;
	  }
	}
	
	//get location of the needed page
	int loc = pageTable[virtpn].location;

	//page is in the executable
	if (loc == 0){
	  //reference to executable
	  OpenFile* exe = currentThread->space->getExe();

	  //reference to in file address
	  int fileAdd = currentThread->space->getFileAdd();
	  
	  //read the page from the executable
	  exe->ReadAt(&(machine->mainMemory[openPage*PageSize]), 
		      PageSize, fileAdd + virtpn * PageSize);
	}

	//page is in the swap file
	if (loc == 1){
	  swapLock->Acquire();
	  //get the location in the swap file from the page table
	  int swapFileLoc = pageTable[virtpn].swapLoc;

	  //read the page from the swap file
	  swapFile->ReadAt(&(machine->mainMemory[openPage * PageSize]),
			   PageSize, swapFileLoc * PageSize);

	  //cout<<"Read: "<<currentThread->space<<" "<<virtpn<<" "<<swapFileLoc<<endl;

	    mySum=0;
	    for (int ijk=0;ijk<PageSize;ijk++) {
	      mySum+=machine->mainMemory[openPage*PageSize+ijk];
	    }
	    //cout<<"CRC read: "<<mySum<<endl;


	  //clear that location to be used by another page
	  swapFileMap->Clear(swapFileLoc);
	  swapLock->Release();
	}
  
	//update pageTable
	pageLock->Acquire();
	pageTable[virtpn].physicalPage = openPage;
	pageTable[virtpn].location = 2;
	pageLock->Release();

	//update ipt
	iptLock->Acquire();
	IPT[openPage].physicalPage = openPage;
	IPT[openPage].virtualPage = virtpn;
	IPT[openPage].valid = true;
	IPT[openPage].use = false;

	//if the page was from the swap file it is dirty
	if (loc == 2 || pageTable[virtpn].dirty == true) {
	  IPT[openPage].dirty = true;
	}else {
	  IPT[openPage].dirty = false;
	}	  

	IPT[openPage].readOnly = false;
	IPT[openPage].ProcessID = currentThread->space;
	IPT[openPage].location = 2;
	iptLock->Release();

	//update tlb
	machine->tlb[currentTLBSpot].virtualPage = virtpn;
	machine->tlb[currentTLBSpot].physicalPage = openPage;
	machine->tlb[currentTLBSpot].valid = true;
	machine->tlb[currentTLBSpot].use = false;

	//if the page was from the swap file it is dirty
	if (loc == 2 || pageTable[virtpn].dirty == true) {
	  machine->tlb[currentTLBSpot].dirty = true;
	}else {
	  machine->tlb[currentTLBSpot].dirty = false;
	}	  

	machine->tlb[currentTLBSpot].readOnly = FALSE;
	
	//update the tlb spot
	currentTLBSpot = (currentTLBSpot + 1) % 4;
      
      }

    interrupt->SetLevel(oldLevel);
    return;
    }

    else {
      cout<<"Unexpected user mode exception - which:"<<which<<"  type:"<< type<<endl;
      interrupt->Halt();
    }
}
